import pandas as pd

from ..pipeline.pipeline import PipeModule, Port, status_manage
from ..pipeline.portTypes import PortTypeHint, PortType
from ..dataclass.terminologies import GeoMaterialProps, Units
from ..geocal.soilCapacityCal import SoilCapacityByExp


@status_manage
class NailCohesionByTest(PipeModule):
    """Calculate pile skin capacity qsik of each soil according to standard."""

    def __init__(self, mname: str = "NailCohesionByTest", auto_run: bool = True,
                 material_table: PortTypeHint.MaterialTable | None = None) -> None:
        """Initialize a NailCohesionByTest object.

        Parameters
        ----------
        material_table : PortTypeHint.MaterialTable, default: None
            The material table for calculation.
            Example:
            ```
            materials_table = {MaterialProp.MaterialID: [],
                                MaterialProp.MaterialName: [],
                                MaterialProp.LiquidIndex: [],
                                MaterialProp.SPTNumber: []}
            units = [null, null, null, null]
            ```
        """
        super().__init__(mname=mname, auto_run=auto_run)

        self._ports_in = {"InputMaterials": Port(ptype=PortType.MaterialTable, data=material_table,
                                                 pdoc="The material table used for calculation.")}
        self._ports_out = {"OutputResults": Port(ptype=PortType.TableData, pdoc="The calculated result.")}

    @property
    def InputMaterials(self) -> None:
        raise AttributeError("Property 'InputMaterials' is write-only.")

    @InputMaterials.setter
    def InputMaterials(self, value: PortTypeHint.MaterialTable | None) -> None:
        self["InputMaterials"] = value

    @property
    def OutputResults(self) -> PortTypeHint.TableData | None:
        return self["OutputResults"]

    def set_cal_params(self, reset: bool = True) -> None:
        return None

    def execute(self) -> PortTypeHint.TableData | None:
        materials: PortTypeHint.MaterialTable | None = self._ports_in["InputMaterials"].data

        if materials is None:
            self._ports_out["OutputResults"].data = None
            return None

        # calculate soil nail cohesion
        results = {GeoMaterialProps.MaterialID: [], GeoMaterialProps.MaterialName: [],
                   "grout": [], "grout_str": [], "steel": [], "steel_str": []}

        for material in materials:
            material_name = material.get(GeoMaterialProps.MaterialName)
            if material_name is None:
                qsk = None
            else:
                cal_qsk = SoilCapacityByExp(material_name=material_name)
                liquid_index = material.get(GeoMaterialProps.LiquidityIndex)
                spt_number = material.get(GeoMaterialProps.SPTNumber)
                qsk = cal_qsk.cal_soil_nail_cohesion_qsk(liquid_index=liquid_index, spt_number=spt_number)

            if qsk is None:
                grout = None
                grout_str = "无法计算"
                steel = None
                steel_str = "无法计算"
            else:
                grout = qsk["grout"][1]
                grout_str = qsk["grout"][0]
                steel = qsk["steel"][1]
                steel_str = qsk["steel"][0]

            material_id = material[GeoMaterialProps.MaterialID]
            results[GeoMaterialProps.MaterialID].append(material_id)
            results[GeoMaterialProps.MaterialName].append(material_name)
            results["grout"].append(grout)
            results["grout_str"].append(grout_str)
            results["steel"].append(steel)
            results["steel_str"].append(steel_str)

        output = PortTypeHint.TableResult(result=pd.DataFrame(results),
                                          units=[Units.null, Units.null, Units.kPa, Units.kPa, Units.kPa, Units.kPa],
                                          ch_names=["岩性ID", "岩土名称", "成孔注浆土钉极限粘结强度", "成孔注浆土钉极限粘结强度-字符",
                                                    "打入钢管土钉极限粘结强度", "打入钢管土钉极限粘结强度-字符"])

        self._ports_out["OutputResults"].data = output
        return output

